#pragma once

#include <string>
#include <fstream>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
#include <boost/random.hpp>

#include "torrent/fwd_decl.hpp"

namespace torrent {

class TrackerClient;
TrackerClientPtr create_tracker_client(Manager& manager, const std::string& announceStr, Job& job);

class TrackerClient
{
public:
	TrackerClient(Manager& manager, const std::string& announceStr, Job& job);
	~TrackerClient(void);

	std::string str();
	void closeLog();
	virtual void start() = 0;
	std::string announceStr() { return _announceStr; }

protected:
	Manager& _manager;
	std::string _announceStr;
	Job& _job;
	std::ofstream _ofs;

	std::string _host;
	std::string _port;
	std::string _requestPath;
};

class HttpTrackerClient	: public TrackerClient, public boost::enable_shared_from_this<HttpTrackerClient>
{
public:
	HttpTrackerClient(Manager& manager, const std::string& announceStr, Job& job);
	~HttpTrackerClient(void) {}

	void start();

private:
	void handleResolve(const boost::system::error_code& err, boost::asio::ip::tcp::resolver::iterator endpointIterator);
	void handleConnect(const boost::system::error_code& err);
	void handleWriteRequest(const boost::system::error_code& err);
	void handleReadStatusLine(const boost::system::error_code& err);
	void handleReadHeaders(const boost::system::error_code& err);
	void handleReadContent(const boost::system::error_code& err);

	boost::asio::ip::tcp::resolver _resolver;
	boost::asio::ip::tcp::socket _socket;
	boost::asio::streambuf _request;
	boost::asio::streambuf _response;
};

class UdpTrackerClient	: public TrackerClient, public boost::enable_shared_from_this<UdpTrackerClient>
{
public:
	UdpTrackerClient(Manager& manager, const std::string& announceStr, Job& job);
	~UdpTrackerClient(void) {}

	void start();

private:
	void handleResolve(const boost::system::error_code& err, boost::asio::ip::udp::resolver::iterator endpointIterator);
	void handleSendConnect(const boost::system::error_code& err, std::size_t bytesTransferred);
	void handleRecvConnect(const boost::system::error_code& err, std::size_t bytesRecvd);
	void handleSendAnnounce(const boost::system::error_code& err, std::size_t bytesTransferred);
	void handleRecvAnnounce(const boost::system::error_code& err, std::size_t bytesRecvd);

	boost::asio::ip::udp::resolver _resolver;
	boost::asio::ip::udp::socket _socket;
	boost::asio::ip::udp::endpoint _endpoint;
	boost::asio::streambuf _request;
	boost::asio::streambuf _response;

	boost::random::mt19937 _gen;

	boost::int64_t _connectionID;
	boost::int32_t _transactionID;
};

}
